unit RtcClDataSetUpdate;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.DBGrids,
  RzDBGrid, RzDBNav, RzButton, RzRadChk, Vcl.StdCtrls, Vcl.Buttons, RzLabel,
  RzPanel, Vcl.ExtCtrls, RzSplit, rtcInfo, rtcDB, Data.DB, kbmMemTable,
  rtcCliModule, DataOperate, Generics.Collections, Datasnap.DBClient;

type
  TframRtcClDataSetUpdate = class(TFrame)
    rzspltr4: TRzSplitter;
    pnl4: TRzPanel;
    btnMtDlData: TSpeedButton;
    lblStatus: TRzLabel;
    chkValidCheck: TCheckBox;
    btnAppUpdate: TBitBtn;
    chbQAppUpdate: TRzCheckBox;
    rzspltr2: TRzSplitter;
    RzDBNavigator3: TRzDBNavigator;
    RzDBGrid3: TRzDBGrid;
    rzspltr3: TRzSplitter;
    RzDBGrid1: TRzDBGrid;
    pnl5: TRzPanel;
    btnRefreshOrder: TSpeedButton;
    RzDBNavigator1: TRzDBNavigator;
    RzDBGrid2: TRzDBGrid;
    pnl6: TRzPanel;
    btnRefreshItem: TSpeedButton;
    RzDBNavigator4: TRzDBNavigator;
    dsDetailItem: TDataSource;
    dsDetailOrder: TDataSource;
    dsMaster: TDataSource;
    rtcdtstmntr1: TRtcDataSetMonitor;
    rtcdtstmntr2: TRtcDataSetMonitor;
    rtcdtstmntr3: TRtcDataSetMonitor;
    cds1: TClientDataSet;
    cds2: TClientDataSet;
    cds3: TClientDataSet;
    procedure RtcMemDataSetDataChange(Sender: TObject);
    procedure btnRefreshItemClick(Sender: TObject);
    procedure btnRefreshOrderClick(Sender: TObject);
    procedure btnMtDlDataClick(Sender: TObject);
    procedure btnAppUpdateClick(Sender: TObject);
    procedure cds1AfterScroll(DataSet: TDataSet);
    procedure cds1BeforeDelete(DataSet: TDataSet);
    procedure cds1BeforePost(DataSet: TDataSet);
    procedure cds2AfterScroll(DataSet: TDataSet);
    procedure cds2BeforeDelete(DataSet: TDataSet);
    procedure cds2BeforePost(DataSet: TDataSet);
    procedure cds2NewRecord(DataSet: TDataSet);
    procedure cds3NewRecord(DataSet: TDataSet);
    procedure cds3BeforePost(DataSet: TDataSet);
  private
    { Private declarations }
  public
    DataOperateFrm: TDataOperate;
    CustNo, OrderNo: integer;
  end;

implementation

{$R *.dfm}

procedure TframRtcClDataSetUpdate.btnAppUpdateClick(Sender: TObject);
var
  Data: TRtcValue;
  tblName: String;
begin
  tblName := 'customer';
  Data := rtcdtstmntr1.ExtractChanges;
  if assigned(Data) then
  begin
    DataOperateFrm.UpdateData(tblName, Data, 'rtcSubmit');
  end;

  tblName := 'orders';
  Data := rtcdtstmntr2.ExtractChanges;
  if assigned(Data) then
  begin
    DataOperateFrm.UpdateData(tblName, Data, 'rtcSubmit');
  end;

  tblName := 'items';
  Data := rtcdtstmntr3.ExtractChanges;
  if assigned(Data) then
  begin
    DataOperateFrm.UpdateData(tblName, Data, 'rtcSubmit');
  end;
end;

procedure TframRtcClDataSetUpdate.btnMtDlDataClick(Sender: TObject);
var
   tmpData:TRtcDataSet;
begin
  { Close Orders and Items DataSets, so the user can't make changes
    to unrelated Orders or Items while waiting for new Customer data ... }
  CustNo := 0;
  cds2.Active := False;
  OrderNo := 0;
  cds3.Active := False;

  { Closing the DataSet for which new data is being requested is NOT
    necessary, but it shows the user that new data is being loaded. }
  cds1.Active := False;
  lblStatus.Caption := 'Requesting Customer data';

  tmpData:= DataOperateFrm.GetRemoteTable('customer', nil);
  rtcdtstmntr1.Active:=False;
  cds1.DisableControls;
  try
    RtcDataSetFieldsToDelphi(tmpData, cds1);
    cds1.CreateDataSet;
    RtcDataSetRowsToDelphi(tmpData, cds1);
  finally
    cds1.EnableControls;
    rtcdtstmntr1.Active:=True;
    cds1.First;
    end;
end;

procedure TframRtcClDataSetUpdate.btnRefreshItemClick(Sender: TObject);
var
  SqlFilter: TList<SqlFilterRd>;
  item: SqlFilterRd;
  tmpData:TRtcDataSet;
begin
  SqlFilter := TList<SqlFilterRd>.Create;
  try
    if OrderNo <> 0 then
    begin
      { Closing the DataSet for which new data is being requested is NOT
        necessary, but it shows the user that new data is being loaded. }
      lblStatus.Caption := 'Requesting Items data, OrderNo=' +
        IntToStr(OrderNo);

      with cds3 do
      begin
        if Active then
          Active := False;
        item.ParmType := 'eq';
        item.Parm := 'OrderNo';
        item.Value := OrderNo;
        SqlFilter.Add(item);
        tmpData := DataOperateFrm.GetRemoteTable('items', SqlFilter);

        rtcdtstmntr3.Active := False;
        DisableControls;
        try
          RtcDataSetFieldsToDelphi(tmpData, cds3);
          cds3.CreateDataSet;
          RtcDataSetRowsToDelphi(tmpData, cds3);
        finally
          EnableControls;
          rtcdtstmntr3.Active := True;
          First;
        end;
      end;
    end
    else
    begin
      { There is no Order info, close the Items dataset. }
      cds3.Active := False;
      lblStatus.Caption := 'No Order selected, Items table closed.';
    end;
  finally
    FreeAndNil(SqlFilter);
  end;
end;

procedure TframRtcClDataSetUpdate.btnRefreshOrderClick(Sender: TObject);
var
  SqlFilter: TList<SqlFilterRd>;
  item: SqlFilterRd;
  tmpData:TRtcDataSet;
begin
  SqlFilter := TList<SqlFilterRd>.Create;
  try
    { Close the Items DataSet, so the user can't make changes
      to unrelated Items while waiting for new Order data ... }
    OrderNo := 0;
    cds3.Active := False;

    if CustNo <> 0 then
    begin
      { Closing the DataSet for which new data is being requested is NOT
        necessary, but it shows the user that new data is being loaded. }
      lblStatus.Caption := 'Requesting Orders data, CustNo=' + IntToStr(CustNo);

      with cds2 do
      begin
        if Active then
          Active := False;
        item.ParmType := 'eq';
        item.Parm := 'CustNo';
        item.Value := CustNo;
        SqlFilter.Add(item);
        tmpData := DataOperateFrm.GetRemoteTable('Orders', SqlFilter);
        rtcdtstmntr2.Active := False;
        DisableControls;
        try
          RtcDataSetFieldsToDelphi(tmpData, cds2);
          cds2.CreateDataSet;
          RtcDataSetRowsToDelphi(tmpData, cds2);
        finally
          EnableControls;
          rtcdtstmntr2.Active := True;
          First;
        end;
      end;

    end
    else
    begin
      { There is no Customer info, close the Orders dataset. }
      cds2.Active := False;
      lblStatus.Caption := 'No Customer selected, Orders table closed.';
    end;
  finally
    FreeAndNil(SqlFilter);
  end;
end;

procedure TframRtcClDataSetUpdate.cds1AfterScroll(DataSet: TDataSet);
var
  NewNo: integer;
begin
  { Customer data received, or the user has scrolled to a new Customer row.
    For our Master/Detail relation with Orders to work,
    we need to refresh our "Orders" data with a new "CustNo". }

  if cds1.Active and (cds1.RecordCount > 0) then
    NewNo := cds1.FieldByName('CustNo').AsInteger
  else
    NewNo := 0;

  if CustNo <> NewNo then
  begin
    CustNo := NewNo;
    btnRefreshOrderClick(nil);
  end;

end;

procedure TframRtcClDataSetUpdate.cds1BeforeDelete(DataSet: TDataSet);
begin
  if not chkValidCheck.Checked then
    Exit;

  { We can leave this job to the Server, but then the user will see
    the record deleted locally and then get an error from the Server. }
  if cds2.RecordCount > 0 then
    raise Exception.Create
      ('Can not delete Customer before deleting all Customer Orders.');
end;

procedure TframRtcClDataSetUpdate.cds1BeforePost(DataSet: TDataSet);
begin
  if not chkValidCheck.Checked then
    Exit;

  { We can leave this job to the Server, but then the user will
    have to refresh the dataset and repeat the last operation in
    case a field was undefined, so we will make this check here. }
  if cds1.FieldByName('CustNo').IsNull then
    raise Exception.Create('Required field "CustNo" is NULL');
end;

procedure TframRtcClDataSetUpdate.cds2AfterScroll(DataSet: TDataSet);
var
  NewNo: integer;
begin
  { Orders data received, or the user has scrolled to a new Order row.
    For our Master/Detail relation with Items to work,
    we need to refresh our "Items" data with a new "OrderNo". }

  if cds2.Active and (cds2.RecordCount > 0) then
    NewNo := cds2.FieldByName('OrderNo').AsInteger
  else
    NewNo := 0;

  if OrderNo <> NewNo then
  begin
    OrderNo := NewNo;
    btnRefreshItemClick(nil);
  end;

end;

procedure TframRtcClDataSetUpdate.cds2BeforeDelete(DataSet: TDataSet);
begin
  if not chkValidCheck.Checked then
    Exit;

  { We can leave this job to the Server, but then the user will see
    the record deleted locally and then get an error from the Server. }
  if cds3.RecordCount > 0 then
    raise Exception.Create
      ('Can not delete Order before deleting all Order Items.');
end;

procedure TframRtcClDataSetUpdate.cds2BeforePost(DataSet: TDataSet);
begin
  if not chkValidCheck.Checked then
    Exit;

  { We can leave this job to the Server, but then the user will
    need to refresh the dataset and repeat the last operation in
    case a field is undefined, so we will make this check here. }
  if cds2.FieldByName('CustNo').AsInteger <> CustNo then
    raise Exception.Create('Required field "CustNo" has invalid value')
  else if cds2.FieldByName('OrderNo').IsNull then
    raise Exception.Create('Required field "OrderNo" is NULL')
  else if cds2.FieldByName('EmpNo').IsNull then
    raise Exception.Create('Required field "EmpNo" is NULL');
end;

procedure TframRtcClDataSetUpdate.cds2NewRecord(DataSet: TDataSet);
begin
  { We should set all "master" fields for a new Order here }
  cds2.FieldByName('CustNo').AsInteger := CustNo;
end;

procedure TframRtcClDataSetUpdate.cds3BeforePost(DataSet: TDataSet);
begin
  if not chkValidCheck.Checked then
    Exit;

  { We can leave this job to the Server, but then the user will
    need to refresh the dataset and repeat the last operation in
    case a field is undefined, so we will make this check here. }
  if cds3.FieldByName('OrderNo').AsInteger <> OrderNo then
    raise Exception.Create('Required field "OrderNo" has invalid value')
  else if cds3.FieldByName('ItemNo').IsNull then
    raise Exception.Create('Required field "ItemNo" is NULL');
end;

procedure TframRtcClDataSetUpdate.cds3NewRecord(DataSet: TDataSet);
begin
  { We should set all "master" fields for a new Item here }
  cds3.FieldByName('OrderNo').AsInteger := OrderNo;
end;


procedure TframRtcClDataSetUpdate.RtcMemDataSetDataChange(Sender: TObject);
var
  Data: TRtcValue;
  tblName: String;
begin
  if chbQAppUpdate.Checked then
    Exit;

  { We are using a single "OnDataChange" implementation for all DataSets }
  if Sender = rtcdtstmntr1 then
  begin
    tblName := 'customer';
    // Refresh Orders if Customer field value has changed
    cds1AfterScroll(nil);
  end
  else if Sender = rtcdtstmntr2 then
  begin
    tblName := 'orders';
    // Refresh Items if Order field value was changed
    cds2AfterScroll(nil);
  end
  else if Sender = rtcdtstmntr3 then
    tblName := 'items';

  Data := TRtcDataSetMonitor(Sender).ExtractChanges;
  if assigned(Data) then
  begin
    lblStatus.Caption := 'Sending ' + tblName + ' changes to the Server ...';
    DataOperateFrm.UpdateData(tblName, Data, 'rtcSubmit');
  end;
end;

end.
